/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package net.db.generator;

import net.db.generator.utils.ClassGeneratorUtil;
import net.db.generator.field.models.BaseFieldModel;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 *
 * @author abosancic
 */
public class JavaBaseClassWriter {

    protected StringBuilder stringClass = null;
    protected String packageName;
    protected String importPackages[];
    protected String className;
    protected String [] uniqueField;
    protected String extendsClassName;
    protected ArrayList<String> implementsInterfaceName;

    protected BaseFieldModel[] fields;
    
    protected String spaces = "    ";
    
    protected boolean jaxb = false;

    public JavaBaseClassWriter() {
      stringClass = new StringBuilder();
    }

    public void generateClass() {
        
        generatePackage();
        generateImports();
        generateHeader();
        generateClassName();
        generateFields();
        generateConstructor();
        generateGettersAndSetters();
        generateToString();
        generateHashCode();
        generateEquals();
        generateFooter();
        // write this class
        writeClass();
    }
    
    protected void generateHeader(){
        stringClass.append("/**\n"
                + " *\n"
                + " * @author " + System.getProperty("user.name") + "\n"
                + " * @date " + new SimpleDateFormat("MMM-dd-yyyy").format(new Date()) + "\n"
                + " */ \n\n");
    }
    
    protected void generateClassName(){
        if (jaxb) {
            stringClass.append("@XmlAccessorType(XmlAccessType.FIELD)").append("\n");
            stringClass.append("@XmlRootElement(name = \"" + getClassName() + "\")").append("\n");
        }
        stringClass.append("public class ").append(ClassGeneratorUtil.humanize(getClassName())).append(getExtendsClassName()).append(getImplementsInterfaceName()).append(" {\n\n");
    }
    
    
    protected void generatePackage(){
       if (packageName != null) {
            stringClass.append("package ").append(getPackageName()).append(";\n\n");
        }
    }
    
    protected void generateImports(){
        if (importPackages != null) {
            for (String string : importPackages) {
                stringClass.append("import ").append(string).append(";\n");
            }
        }
    }
    
    protected void generateConstructor(){
        stringClass.append(spaces).append("public ").append(ClassGeneratorUtil.humanize(getClassName())).append("() {\n");
        stringClass.append(spaces).append(spaces).append("super();\n");
        stringClass.append(spaces).append("}\n\n");
    }
    
    protected void generateFooter(){
         stringClass.append("}\n");
    }

    protected void generateFields() {

        for (BaseFieldModel fieldModel : fields) {
            String field = fieldModel.getFieldName();
            String type = fieldModel.getFieldType();
            if (jaxb && fieldModel.isPrimaryKey()) {               
                stringClass.append(spaces).append("@XmlAttribute(name = \"" + ClassGeneratorUtil.humanizeToField(field) + "\")").append("\n");
            }
            stringClass.append(spaces).append("private ").append(type).append(" ").append(ClassGeneratorUtil.humanizeToField(field)).append(";\n\n");
        }
    }
    
    protected final synchronized void writeClass() {

        try {
            String path = "." + File.separator + "src" + File.separator;
            if (getPackageName() != null) {
                path += getPackageName().replace(".", File.separator);
                new File(path).mkdirs();
            }

            FileWriter fstream = new FileWriter(path + File.separator + ClassGeneratorUtil.humanize(getClassName()) + ".java");
            BufferedWriter out = new BufferedWriter(fstream);
            out.write(stringClass.toString());
            //Close the output stream
            out.close();
        } catch (Exception e) {//Catch exception if any
            System.err.println("Error: " + e.getMessage());
        }
        
        stringClass = new StringBuilder();
    }

    // setters and getters
    public String getClassName() {
        return className;
    }

    public void setClassName(String className) {
        this.className = className;
    }

    public BaseFieldModel[] getFields() {
        return fields;
    }

    public void setFields(BaseFieldModel[] fields) {
        this.fields = fields;
    }

    public void setFields(List<BaseFieldModel> fields) {
        this.fields = fields.toArray(new BaseFieldModel[fields.size()]);
    }

    public String getPackageName() {
        return packageName;
    }

    public void setPackageName(String packageName) {
        this.packageName = packageName;
    }

    public String[] getImportPackages() {
        return importPackages;
    }

    public void setImportPackages(String[] importPackages) {
        this.importPackages = importPackages;
    }

    public String [] getUniqueField() {
        return uniqueField;
    }

    public void setUniqueField(String [] uniqueField) {
        this.uniqueField = uniqueField;
    }

    protected void generateGettersAndSetters() {
        
        String override = "@Override";
        for (BaseFieldModel fieldModel : fields) {
            
            String field = fieldModel.getFieldName();
            String type = fieldModel.getFieldType();
            
            //add annotations @Override
            if (getImplementsInterfaceName().length() > 1) {
                stringClass.append(spaces).append(override).append("\n");
            }
            stringClass.append(spaces).append("public ").append(type).append(" ").append(ClassGeneratorUtil.humanizeToGetter(field)).append("() {\n");
            stringClass.append(spaces).append(spaces).append("return ").append("this.").append(ClassGeneratorUtil.humanizeToField(field)).append(";\n");
            stringClass.append(spaces).append("}\n\n");

            //add annotations @Override
            if (getImplementsInterfaceName().length() > 1) {
                stringClass.append(spaces).append(override).append("\n");
            }
            stringClass.append(spaces).append("public void ").append(ClassGeneratorUtil.humanizeToSetter(field)).append("(").append(type).append(" ").append(ClassGeneratorUtil.humanizeToField(field)).append("){\n");
            stringClass.append(spaces).append(spaces).append("this.").append(ClassGeneratorUtil.humanizeToField(field)).append(" = ").append(ClassGeneratorUtil.humanizeToField(field)).append(";\n");
            stringClass.append(spaces).append("}\n\n");
        }
        
    }

    protected void generateToString() {
        
        StringBuilder toStringMethod = new StringBuilder();
        
        for (BaseFieldModel fieldModel : fields) {
            String field = fieldModel.getFieldName();
            field = ClassGeneratorUtil.humanizeToField(fieldModel.getFieldName());
            String tmp = "\"" + field + "=\" + " + field + " + \", \" + ";
            toStringMethod.append(tmp);
        }
        
        stringClass.append(spaces).append("@Override\n");
        stringClass.append(spaces).append("public String toString() {\n");
        if (toStringMethod.length() > 7) {
            stringClass.append(spaces).append(spaces).append("return \"").append(ClassGeneratorUtil.humanize(className)).append("{\" + ").append(toStringMethod.substring(0, toStringMethod.length() - 7)).append("\"}\";\n");
        } else {
            stringClass.append(spaces).append(spaces).append("return ").append("\"").append(ClassGeneratorUtil.humanize(className)).append("").append("}\";\n");
        }
        stringClass.append(spaces).append("}\n\n");
    }

    protected void generateHashCode() {
        
        if (uniqueField != null) {
            stringClass.append(spaces).append("@Override\n");
            stringClass.append(spaces).append("public ").append("int").append(" ").append("hashCode").append("() {\n");
            stringClass.append(spaces).append(spaces).append("int hash = 0;\n");
            int num = uniqueField.length;
            for (int i = 0; i < num; i++) {
                stringClass.append(spaces).append(spaces).append("hash += java.util.Objects.hashCode(").append(ClassGeneratorUtil.humanizeToField(uniqueField[i])).append(");\n");
            }
            stringClass.append(spaces).append(spaces).append("return hash").append(";\n");
            stringClass.append(spaces).append("}\n\n");
        }
    }
    
    protected void generateEquals() {
        
        if (uniqueField != null) {
            stringClass.append(spaces).append("@Override\n");
            stringClass.append(spaces).append("public ").append("boolean").append(" ").append("equals").append("(Object object) {\n");
            stringClass.append(spaces).append(spaces).append("// TODO: Warning - this method won't work in the case the id fields are not set\n");
            stringClass.append(spaces).append(spaces).append("if (!(object instanceof ").append(ClassGeneratorUtil.humanize(getClassName())).append(")){\n");
            stringClass.append(spaces).append(spaces).append(spaces).append("return false").append(";\n");
            stringClass.append(spaces).append(spaces).append("}").append("\n");
            stringClass.append(spaces).append(spaces).append(ClassGeneratorUtil.humanize(getClassName())).append(" other = (").append(ClassGeneratorUtil.humanize(getClassName())).append(") object").append(";\n");
            int num = uniqueField.length;
            for (int i = 0; i < num; i++) {
                stringClass.append(spaces).append(spaces).append("if (!java.util.Objects.equals(this.").append(ClassGeneratorUtil.humanizeToField(uniqueField[i])).append(", other.").append(ClassGeneratorUtil.humanizeToField(uniqueField[i])).append(")) {\n");
                stringClass.append(spaces).append(spaces).append(spaces).append("return false").append(";\n");
                stringClass.append(spaces).append(spaces).append("}").append("\n");
            }
            stringClass.append(spaces).append(spaces).append("return true").append(";\n");
            stringClass.append(spaces).append("}\n\n");
        }
        
    }
    
    public String getExtendsClassName() {
        return extendsClassName == null ? "" : " extends " + extendsClassName;
    }

    public void setExtendsClassName(String extendsClassName) {
        this.extendsClassName = extendsClassName;
    }
    
    public String getImplementsInterfaceName() {
        String interfaces = "";
        
        if (implementsInterfaceName != null) {
            for (String inter : implementsInterfaceName) {
                interfaces += ClassGeneratorUtil.humanize( inter ) + ", ";
            }

            interfaces = interfaces.substring(0, interfaces.length() - 2);
        }
        return interfaces.equals("") ? "" : " implements " + interfaces;
    }

    public void addInterfaceName(String interfaceName) {
        if(this.implementsInterfaceName == null){
            this.implementsInterfaceName = new ArrayList<String>();
        }
        this.implementsInterfaceName.add(interfaceName);
    }
    
    public void setJaxb(boolean jaxb) {
        this.jaxb = jaxb;
    }
    
}
